/*
 * Copyright (c) 2011-2012 Alexander Dubu
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * o  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * o  Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * o  Neither the name Axil nor the names of its contributors may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package axil.compiler.lexer;

import axil.api.extend.Source;
import axil.stdlib.numbers.type.Fraction;
import axil.stdlib.numbers.type.Int;
import axil.stdlib.numbers.type.Num;
import axil.stdlib.numbers.type.Percent;

import java.math.BigDecimal;
import java.math.BigInteger;

import static axil.framework.Functions.*;

/**
 * A token representing a literal value. The literal can have a prefix and
 * suffix. This does not comprehensively cover all possible literals, only those
 * that must be uniquely recognized by the lexical analyzer.
 */
public class Literal extends Token {
	public static final char NONE = '\u0000';

	public enum Species {
		INTEGER,
		DECIMAL,
		PERCENT,
		FRACTION,
		STRING,
		CHAR,
		DATE,
		TIME,
		MONEY
	}

	private char prefix;
	private String suffix;
	private Species species;
	private Object value;


	public Literal(Source source, Species species, Object value,
	               char prefix, String suffix) {
		super(source);
		this.prefix = prefix;
		this.suffix = suffix;
		this.species = species;
		this.value = value;
	}


	public Literal(Source source, Species species, Object value) {
		this(source, species, value, NONE, null);
	}


	/**
	 * Get the type of this token.
	 */
	public Type type() {
		return Type.literal;
	}


	public boolean percent() {
		return (species == Species.INTEGER) ||
		       (species == Species.DECIMAL) ||
		       (species == Species.FRACTION);
	}


	public Literal percentify() {
		switch (species) {

		case DECIMAL:
		case INTEGER:
			value = new Percent(Num.convert((Number)value).divide(Int.from(100)));
			break;

		case FRACTION:
			// 1/2% is stored as 1/200 (0.005) when a fraction
			Fraction f = (Fraction)value;
			value = new Percent(f.divide(Int.from(100)));
			break;

		default:
			throw unreachable();
		}
		this.species = Species.PERCENT;
		return this;
	}


	public boolean extended() {
		return (prefix != NONE) || ! empty(suffix);
	}
	public char prefix() {
		return prefix;
	}
	public Literal prefix(char prefix) {
		this.prefix = prefix;
		return this;
	}
	public Literal suffix(String suffix) {
		this.suffix = suffix;
		return this;
	}
	public String suffix() {
		return suffix;
	}
	public Species species() {
		return species;
	}
	public Object value() {
		return value;
	}
	public BigDecimal decimal() {
		return (BigDecimal)value;
	}
	public BigInteger integer() {
		return (BigInteger)value;
	}
	public String text() {
		return (String)value;
	}


	/**
	 * Return a string representation of this object. The string returned is
	 * never null.
	 */
	public String toString() {
		return species.toString() + ' ' +
		       (prefix()  == NONE ? "" : Character.toString(prefix)) +
		       str(value) +
		       (suffix == null ? "" : suffix);
	}
}
